use std::path::Path;
use std::fs;
use std::io::{BufRead, BufReader, Write};

use anyhow::{Ok, Result};
use ignore::WalkBuilder;
use tokio::{self, runtime::Runtime, sync};

pub mod fd;

pub fn copy(source: &str, target: &str) -> Result<()> {
    let mut walk_builder: WalkBuilder = WalkBuilder::new(source);

    walk_builder.hidden(false);

    let mut paths = Vec::new();
    let mut floders = Vec::new();
    for result in walk_builder.build() {
        let std::result::Result::Ok(entry) = result else {
            continue;
        };

        if !entry.path().is_file() {
            floders.push(String::from(entry.path().to_str().unwrap()));
            continue;
        }

        paths.push(String::from(entry.path().to_str().unwrap()));
    }

    let rt = Runtime::new().unwrap();
    let target_clone = String::from(target);
    rt.block_on(async {
        if floders.is_empty() {
            return;
        }

        let (tx, mut rx) = sync::mpsc::channel::<bool>(floders.len());
        for path in floders.iter() {
            let async_path = path.clone();
            let path_len = source.len();
            let t_c = target_clone.clone();
            let tx_clone: sync::mpsc::Sender<bool> = tx.clone();
            tokio::spawn(async move {
                let target_path = format!("{}{}", &t_c, &async_path[path_len..]);
                if !Path::new(&target_path).is_dir() {
                    std::fs::create_dir_all(target_path).expect("Error : create_dir_all failed.");
                }

                if tx_clone.send(true).await.is_err() {
                    eprintln!("    Error[copy] : create_dir_all send error");
                }
            });
        }

        drop(tx);

        while let Some(_) = rx.recv().await {}
    });

    rt.block_on(async {
        if paths.is_empty() {
            return;
        }

        let (tx, mut rx) = sync::mpsc::channel::<bool>(paths.len());
        for path in paths.iter() {
            let async_path = path.clone();
            let path_len = source.len();
            let t_c = target_clone.clone();
            let tx_clone: sync::mpsc::Sender<bool> = tx.clone();
            tokio::spawn(async move {
                let target_path = format!("{}{}", &t_c, &async_path[path_len..]);
                //println!("path : {}", &target_path);
                std::fs::copy(async_path, target_path).expect("Error : copy failed.");

                if tx_clone.send(true).await.is_err() {
                    eprintln!("    Error[copy] : copy send error");
                }
            });
        }

        drop(tx);

        while let Some(_) = rx.recv().await {}
    });

    anyhow::Ok(())
}

pub fn crlf2lf(source: &str) -> Result<()> {
    let mut walk_builder: WalkBuilder = WalkBuilder::new(source);
    walk_builder.add_ignore("**.meta");
    let mut paths = vec![];
    for result in walk_builder.build() {
        let std::result::Result::Ok(entry) = result else {
            continue;
        };

        if !entry.path().is_file() {
            continue;
        }
        
        if !Path::new(entry.file_name()).extension().unwrap().eq("txt") {
            continue;
        }

        paths.push(String::from(entry.path().to_str().unwrap()));
    }


    let rt = Runtime::new().unwrap();
    rt.block_on(async {
        if paths.is_empty() {
            return;
        }

        let (tx, mut rx) = sync::mpsc::channel::<bool>(paths.len());
        for path in paths.iter() {
            let async_path = path.clone();
            let tx_clone: sync::mpsc::Sender<bool> = tx.clone();
            tokio::spawn(async move {
                let file = fs::File::open(&async_path).unwrap();
                let reader = BufReader::new(file);
                let lines: Vec<String> = reader.lines().map(|l| l.unwrap().replace("\r\n", "\n")).collect();

                let mut output = fs::File::create(&async_path).unwrap();
                for line in lines {
                    writeln!(output, "{}", line).unwrap();
                }

                if tx_clone.send(true).await.is_err() {
                    eprintln!("    Error[copy] : create_dir_all send error");
                }
            });
        }

        drop(tx);

        while let Some(_) = rx.recv().await {}
    });

    anyhow::Ok(())
}

pub fn remove(source: &str) -> Result<()> {
    let mut walk_builder: WalkBuilder = WalkBuilder::new(source);

    walk_builder.hidden(false);

    let mut paths = Vec::new();
    let mut floders = Vec::new();
    for result in walk_builder.build() {
        let std::result::Result::Ok(entry) = result else {
            continue;
        };

        if !entry.path().is_file() {
            floders.push(String::from(entry.path().to_str().unwrap()));
            continue;
        }

        paths.push(String::from(entry.path().to_str().unwrap()));
    }

    let rt = Runtime::new().unwrap();
    rt.block_on(async {
        if paths.is_empty() {
            return;
        }

        let (tx, mut rx) = sync::mpsc::channel::<bool>(paths.len());
        for path in paths.iter() {
            let async_path = path.clone();
            let tx_clone: sync::mpsc::Sender<bool> = tx.clone();
            tokio::spawn(async move {
                std::fs::remove_file(async_path).expect("Error : remove_file failed.");

                if tx_clone.send(true).await.is_err() {
                    eprintln!("    Error[remove] : remove_file send error");
                }
            });
        }

        drop(tx);

        while let Some(_) = rx.recv().await {}
    });

    rt.block_on(async {
        let async_path = String::from(source);
        tokio::spawn(async move {
            //println!("{}", &async_path);
            std::fs::remove_dir_all(async_path).expect("Error : remove_dir failed.");
        })
        .await
        .unwrap();
    });

    anyhow::Ok(())
}
